/*******************************************************************************
 * Copyright (c) 2006 CEA List.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     CEA List - initial API and implementation
 *******************************************************************************/
package com.cea.papyrus.diagram.component.command;

import org.eclipse.draw2d.PositionConstants;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Insets;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.gef.commands.Command;
import org.eclipse.uml2.uml.Component;
import org.eclipse.uml2.uml.Interface;

import com.cea.papyrus.core.helper.GraphEdgeHelper;
import com.cea.papyrus.core.helper.NodeHelper;
import com.cea.papyrus.diagram.component.helper.NodeInterfaceHelper;
import com.cea.papyrus.diagraminterchange2.di2.Di2Factory;
import com.cea.papyrus.diagraminterchange2.di2.DiagramElement;
import com.cea.papyrus.diagraminterchange2.di2.GraphConnector;
import com.cea.papyrus.diagraminterchange2.di2.GraphEdge;
import com.cea.papyrus.diagraminterchange2.di2.GraphElement;
import com.cea.papyrus.diagraminterchange2.di2.GraphNode;
import com.cea.papyrus.diagraminterchange2.di2.Uml1SemanticModelBridge;

/**
 * This command is used to add a representation of a provided or required Interface into the diagram
 */
public class InterfaceDisplayCommand extends Command {

	protected static int DISTANCE=100;
	protected String name;
	//private BehavioralFeature parent;
	/**
	 * maybe this is the nameLable of the port od the graphnode that represents the port.
	 */
	protected DiagramElement elementSource;

	/**
	 * 
	 */
	protected NodeHelper factory;

	/**
	 * 
	 */
	protected GraphNode interfaceNode;
	protected Interface anInterface;

	/**
	 * 
	 */
	protected DiagramElement parent;

	/**
	 * 
	 * 
	 * @param elementSource 
	 * @param name 
	 * @param parent 
	 */
	public InterfaceDisplayCommand(String name, DiagramElement elementSource,Interface anInterface, DiagramElement parent) {
		factory = new NodeInterfaceHelper();
		setLabel("Display Interface"+name);
		this.name = name;
		this.elementSource = elementSource;
		this.parent = parent;
		this.anInterface=anInterface;
		
		// If elementSource is a port or a component, parent should be elementSource.getContainer()
//		if (((Uml1SemanticModelBridge)((GraphNode)elementSource).getSemanticModel()).getElement() instanceof Component ||		
//			    ((Uml1SemanticModelBridge)((GraphNode)elementSource).getSemanticModel()).getElement() instanceof Port) {	
//				this.parent= elementSource.getContainer();
//			}
	}


	/**
	 * Creates a new InterfaceDisplayCommand
	 * @param elementSource the source diagram element
	 * @param anInterface the interface to display
	 * @param parent the parent diagram element
	 */
	public InterfaceDisplayCommand(DiagramElement elementSource,Interface anInterface, DiagramElement parent) {
		this(anInterface.getName(), elementSource, anInterface,parent);
	}


	/* (non-Javadoc)
	 * @see org.eclipse.gef.commands.Command#execute()
	 */
	/**
	 * 
	 */
	@Override
	public void execute() {
		if(canExecute()){
			if (((Uml1SemanticModelBridge)((GraphNode)elementSource).getSemanticModel()).getElement() instanceof Component){
				// Connection directly to component
				//1. We have to place the anchor point.
				//   Adapted from composite connection to port.
				//   Position is Forced: Provided ==> EAST
				//                       Required ==> WEST

				Point interfacePosition; //position of the interface
				Point sourcePos=null;    // position of the source anchor of the link
				Point targetPos=null;    // position of the target anchor of the link
				GraphEdge edge=null ;    // link between the interface and the component
				GraphEdgeHelper ge= new GraphEdgeHelper();

				// 	Here position is forced to east of component

				interfacePosition= new Point(((GraphElement)elementSource).getPosition().x+((GraphNode)elementSource).getSize().width + DISTANCE, 
						((GraphElement)elementSource).getPosition().y + ((GraphNode)elementSource).getSize().height/2- 10);
				//creation of the interface
				interfaceNode = factory.createGraphElement(anInterface,(GraphElement)elementSource,interfacePosition,new Dimension(20,20));			
				interfaceNode.setSemanticParent(elementSource);	
				//creation of the graphEdge
				
				
				edge = (GraphEdge)ge.createGraphElement(null, (GraphElement)parent, null, null);
				
				sourcePos = new Point(interfaceNode.getPosition().x, 
						interfaceNode.getPosition().y+interfaceNode.getSize().height/2);
				targetPos = new Point(((GraphElement)elementSource).getPosition().x+((GraphNode)elementSource).getSize().width, 
						((GraphElement)elementSource).getPosition().y+((GraphNode)elementSource).getSize().height/2);
				// create source anchor/connector
				GraphConnector srcConnector = Di2Factory.eINSTANCE.createGraphConnector();
				srcConnector.setPosition(targetPos);
				// create target connector
				GraphConnector targetConnector = Di2Factory.eINSTANCE.createGraphConnector();
				targetConnector.setPosition(sourcePos);
				// connect to graphEdge [0]=source  [1]=target
				edge.getAnchor().add(srcConnector);
				edge.getAnchor().add(targetConnector);
				edge.setSemanticParent(interfaceNode);
				getSourceAnchor(edge).setGraphElement((GraphElement)elementSource);
				getTargetAnchor(edge).setGraphElement(interfaceNode);
				edge.setContainer((GraphElement)elementSource.getContainer());
				interfaceNode.setSemanticParent(elementSource);			
			}
			else {
				// Connection to a port
				//1.Calculate inside rectangle of the part that contains the port.
				Rectangle insideRect= new Rectangle(((GraphNode)(elementSource.getSemanticParent())).getPosition().getCopy(),
						((GraphNode)(elementSource.getSemanticParent())).getSize().getCopy());
				insideRect.crop(new Insets(1));
				//2. Calculate bounds of the port
				Rectangle portBound=new Rectangle(((GraphNode)elementSource).getPosition().getCopy(),
						((GraphNode)elementSource).getSize().getCopy());
				//3. Detection of the orientation of the portNode

				int portOrientation=insideRect.getPosition(portBound.getCenter());

				//4. we have to place the port by taking into account port orientation.
				Point interfacePosition; 	//position of the interface
				Point sourcePos=null; 		// position of the source anchor of the link
				Point targetPos=null;		// position of the target anchor of the link
				GraphEdge edge=null ;		// link between the interface and the port
				GraphEdgeHelper ge= new GraphEdgeHelper();

				switch (portOrientation){
				case PositionConstants.EAST:
					//calculus of the position of the interface.
					interfacePosition= new Point(((GraphElement)elementSource).getPosition().x+DISTANCE, 
							((GraphElement)elementSource).getPosition().y);
					//creation of the interface
					interfaceNode=factory.createGraphElement(anInterface,(GraphElement)elementSource,interfacePosition,new Dimension(40,40));

					//creation of the graphEdge

					edge = (GraphEdge)ge.createGraphElement(null, (GraphElement)parent, null, null);

					sourcePos = new Point(interfaceNode.getPosition().x, 
							interfaceNode.getPosition().y+interfaceNode.getSize().height/2);
					targetPos = new Point(((GraphElement)elementSource).getPosition().x+((GraphNode)elementSource).getSize().width, 
							((GraphElement)elementSource).getPosition().y+((GraphNode)elementSource).getSize().height/2);
					break;
				case PositionConstants.WEST:
//					calculus of the position of the interface.
					interfacePosition= new Point(((GraphElement)elementSource).getPosition().x-DISTANCE, 
							((GraphElement)elementSource).getPosition().y);
					//creation of the interface
					interfaceNode=factory.createGraphElement(anInterface,(GraphElement)elementSource,interfacePosition,new Dimension(40,40));
			
					//creation of the graphEdge
					edge = (GraphEdge)ge.createGraphElement(null, (GraphElement)parent, null, null);

					sourcePos = new Point(interfaceNode.getPosition().x+interfaceNode.getSize().width, 
							interfaceNode.getPosition().y+interfaceNode.getSize().height/2);
					targetPos = new Point(((GraphElement)elementSource).getPosition().x, 
							((GraphElement)elementSource).getPosition().y+((GraphNode)elementSource).getSize().height/2);
					break;
				case PositionConstants.NORTH:
					// calculus of the position of the interface.
					interfacePosition= new Point(((GraphElement)elementSource).getPosition().x, 
							((GraphElement)elementSource).getPosition().y-DISTANCE);
					// creation of the interface

					interfaceNode=factory.createGraphElement(anInterface,(GraphElement)elementSource,interfacePosition,new Dimension(40,40));
					// creation of the graphEdge
					edge = (GraphEdge)ge.createGraphElement(null, (GraphElement)parent, null, null);

					sourcePos = new Point(interfaceNode.getPosition().x+interfaceNode.getSize().width/2, 
							interfaceNode.getPosition().y+interfaceNode.getSize().height);
					targetPos = new Point(((GraphElement)elementSource).getPosition().x+((GraphNode)elementSource).getSize().width/2, 
							((GraphElement)elementSource).getPosition().y);
					break;

				case PositionConstants.SOUTH:
					// calculus of the position of the interface.
					interfacePosition= new Point(((GraphElement)elementSource).getPosition().x, 
							((GraphElement)elementSource).getPosition().y+DISTANCE);
					//creation of the interface

					interfaceNode=factory.createGraphElement(anInterface,(GraphElement)elementSource,interfacePosition,new Dimension(40,40));
					//creation of the graphEdge
					edge = (GraphEdge)ge.createGraphElement(null, (GraphElement)parent, null, null);

					sourcePos = new Point(interfaceNode.getPosition().x+interfaceNode.getSize().width/2, 
							interfaceNode.getPosition().y);
					targetPos = new Point(((GraphElement)elementSource).getPosition().x+((GraphNode)elementSource).getSize().width/2, 
							((GraphElement)elementSource).getPosition().y+((GraphNode)elementSource).getSize().height);
					break;
				}
				// create source anchor/connector
				GraphConnector srcConnector = Di2Factory.eINSTANCE.createGraphConnector();
				srcConnector.setPosition(targetPos);
				// create target connector
				GraphConnector targetConnector = Di2Factory.eINSTANCE.createGraphConnector();
				targetConnector.setPosition(sourcePos);
				// connect to graphEdge [0]=source  [1]=target
				edge.getAnchor().add(srcConnector);
				edge.getAnchor().add(targetConnector);
				edge.setSemanticParent(interfaceNode);

				getSourceAnchor(edge).setGraphElement((GraphElement)elementSource);
				getTargetAnchor(edge).setGraphElement(interfaceNode);
				edge.setContainer(((GraphElement)elementSource).getContainer());
			}
		}
	}



	/**
	 * Computes the middle point between p1 and p2.
	 * 
	 * @param p1 
	 * @param p2 
	 * 
	 * @return 
	 */
	protected Point middlePoint(Point p1, Point p2){
		Dimension d = p2.getDifference(p1);
		return new Point(p1.x + d.width / 2, p1.y + d.height / 2);
	}

	/**
	 * 
	 * 
	 * @param edge 
	 * 
	 * @return The target anchor of the specified edge.
	 */
	protected GraphConnector getTargetAnchor(GraphEdge edge) {
		return ((GraphConnector)edge.getAnchor().get(1));
	}

	/**
	 * 
	 * 
	 * @param edge 
	 * 
	 * @return The source anchor of the specified edge.
	 */
	protected GraphConnector getSourceAnchor(GraphEdge edge) {
		return ((GraphConnector)edge.getAnchor().get(0));
	}


	/* (non-Javadoc)
	 * @see org.eclipse.gef.commands.Command#redo()
	 */
	/**
	 * 
	 */
//	@Override
//	public void redo() {
//		execute();
//	}

	/* (non-Javadoc)
	 * @see org.eclipse.gef.commands.Command#undo()
	 */
	/**
	 * 
	 */
//	@Override
//	public void undo() {
//
//	}

	/**
	 * This method tests if the graphNode is allowed to be created.
	 * 
	 * @return 
	 */
	@Override
	public boolean canExecute() {

		return true;	

	}

}